/*
 * Copyright 2007-2010 Jeremy Brown.
 *
 * Licensed under the Apache License, Version 2.0 (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @author <a href="http://jeremy-brown.com">Jeremy Brown</a>
 */

package groovy.prefuse.demos

import java.awt.*
import java.awt.event.*
import java.awt.geom.*

import javax.swing.*

import prefuse.*
import prefuse.action.*
import prefuse.action.assignment.*
import prefuse.action.layout.*
import prefuse.action.layout.graph.*
import prefuse.activity.*
import prefuse.controls.*
import prefuse.data.*
import prefuse.data.io.*
import prefuse.data.tuple.*
import prefuse.render.*
import prefuse.util.*
import prefuse.util.force.*
import prefuse.util.ui.*
import prefuse.visual.*
import prefuse.visual.sort.*

import groovy.swing.*
import groovy.prefuse.*
import groovy.prefuse.factory.*
import groovy.prefuse.impl.action.assignment.*
import groovy.prefuse.impl.visual.sort.*

/**
 * @author <a href="http://jheer.org">jeffrey heer</a>
 */
public class DataMountain extends Display 
{
	static PrefuseBuilder swing
	static String dataFile = "data/amazon.txt";
	static String imageDirectory = "data/images/"
   
    public static void main(String[] args)
	{
		UILib.setPlatformLookAndFeel();
		
		swing = new PrefuseBuilder()
		swing.registerPrefuse()

		demo()
    }
	
    public static void demo() 
	{
		swing.build()
		{
			frame(id:"frame", title:"p r e f u s e  |  d a t a m o u n t a i n", defaultCloseOperation:javax.swing.WindowConstants.EXIT_ON_CLOSE)
			{
				pfDisplay(id:"display", size:[640,450], damageRedraw:false, border:[30,20,5,20], 
					backgroundImage:["data/backgrounds/Dock.jpg", true, false],
					itemSorter:new ClosureItemSorter({item-> return (int)(10000*item.getY());})
                	)
				{
					pfControlListener(new DataMountainControl())
					pfVisualization(id:"vis", defaultGroup:"data")
					{
						pfTable(id:"table", file:"$dataFile", hasHeader:true)
						{
							pfColumn("image", expression:"CONCAT('$imageDirectory', id, '.01.MZZZZZZZ.jpg')")
						}
						
						pfLabelRenderer(id:"nodeRenderer", textField:null, imageField:"image", valign:Constants.BOTTOM, horizontalPadding:0, verticalPadding:0, maxImageDimensions:[100,100])
						
						pfActionList(id:"init", name:"init")
						{
							pfRandomLayout()
							pfSizeAction(id:"dataMountainSizeAction", closure:{item-> double y = item.getEndY(); return 0.2 + y/1400;})
						}
						
						pfActionList(id:"update", name:"update")
						{
							pfAction(dataMountainSizeAction)
							pfColorAction(field:VisualItem.STROKECOLOR, closure:{item-> return ColorLib.rgb((item.isHover() ? 255 : 0), 0, 0);})
							pfRepaintAction()
						}

						// we run this to make sure the forces are stabilized
						pfActionList(id:"preforce", name:"preforce", duration:1000)
						{
							pfLayout(new DataMountainForceLayout(true))
						}

						// this will cause docs to move out of the way when dragging
						pfActionList(id:"forces", name:"forces", duration:Activity.INFINITY)
						{
							pfLayout(new DataMountainForceLayout(false), id:"fl")
							pfAction(update) //update is the pfActionList declared above
							pfActivityListener(activityCanceled: {activity-> fl.reset()})
						}
					}
				}
			}
		}

		// pre-load images, otherwise they will be loaded asynchronously
		swing.nodeRenderer.getImageFactory().preloadImages(swing.vis.items(),"image");

		//initialize and present the interface
		swing.vis.run("init");
		swing.vis.runAfter("preforce", "update");
		swing.vis.run("preforce");

		swing.frame.pack()
		swing.frame.setVisible(true)
    }
} // end of class DataMountain



//The classes below (originally inner classes) are mostly unchanged from the original POJO demo. Nothing Groovy to see.



public class DataMountainForceLayout extends ForceDirectedLayout 
{
	public DataMountainForceLayout(boolean enforceBounds) {
		super("data",enforceBounds,false);
		
		ForceSimulator fsim = new ForceSimulator();
		fsim.addForce(new NBodyForce(-0.4f, 25f, NBodyForce.DEFAULT_THETA));
		fsim.addForce(new SpringForce(1e-5f,0f));
		fsim.addForce(new DragForce());
		setForceSimulator(fsim);
		
		m_nodeGroup = "data";
		m_edgeGroup = null;
	}
	
	protected float getMassValue(VisualItem n) {
		return n.isHover() ? 5f : 1f;
	}

	public void reset() {
		Iterator iter = m_vis.visibleItems(m_nodeGroup);
		while ( iter.hasNext() ) {
			VisualItem item = (VisualItem)iter.next();
			ForceItem aitem = (ForceItem)item.get(ANCHORITEM);
			if ( aitem != null ) {
				aitem.location[0] = (float)item.getEndX();
				aitem.location[1] = (float)item.getEndY();
			}
		}
		super.reset();
	}
	protected void initSimulator(ForceSimulator fsim) 
	{
		// make sure we have force items to work with
		TupleSet t = (TupleSet)m_vis.getGroup(m_group);
		t.addColumns(ANCHORITEM_SCHEMA);
		t.addColumns(FORCEITEM_SCHEMA);
		
		Iterator iter = m_vis.visibleItems(m_nodeGroup);
		while ( iter.hasNext() ) {
			VisualItem item = (VisualItem)iter.next();
			// get force item
			ForceItem fitem = (ForceItem)item.get(FORCEITEM);
			if ( fitem == null ) {
				fitem = new ForceItem();
				item.set(FORCEITEM, fitem);
			}
			fitem.location[0] = (float)item.getEndX();
			fitem.location[1] = (float)item.getEndY();
			fitem.mass = getMassValue(item);
			
			// get spring anchor
			ForceItem aitem = (ForceItem)item.get(ANCHORITEM);
			if ( aitem == null ) {
				aitem = new ForceItem();
				item.set(ANCHORITEM, aitem);
				aitem.location[0] = fitem.location[0];
				aitem.location[1] = fitem.location[1];
			}
			
			fsim.addItem(fitem);
			fsim.addSpring(fitem, aitem, 0);
		}
	}
	
    public static final String ANCHORITEM = "_anchorItem";
    public static final Schema ANCHORITEM_SCHEMA = new Schema();
    static 
	{
        ANCHORITEM_SCHEMA.addColumn(ANCHORITEM, ForceItem.class);
    }
} // end of inner class DataMountainForceLayout









public class DataMountainControl extends ControlAdapter {
	public static final String URL = "http://www.amazon.com/exec/obidos/tg/detail/-/";
	private VisualItem activeItem;
	private Point2D down = new Point2D.Double();
	private Point2D tmp = new Point2D.Double();
	private boolean wasFixed, dragged;
	private boolean repaint = false;
	
	public void itemEntered(VisualItem item, MouseEvent e) {
		Display d = (Display)e.getSource();
		d.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
		d.setToolTipText(item.getString("id"));
		activeItem = item;
		wasFixed = item.isFixed();
	}
	
	public void itemExited(VisualItem item, MouseEvent e) {
		if ( activeItem == item ) {
			activeItem = null;
			item.setFixed(wasFixed);
		}
		Display d = (Display)e.getSource();
		d.setToolTipText(null);
		d.setCursor(Cursor.getDefaultCursor());
	}
	
	public void itemPressed(VisualItem item, MouseEvent e) {
		if (!SwingUtilities.isLeftMouseButton(e)) return;
		
		// set the focus to the current node
		Visualization vis = item.getVisualization();
		vis.getFocusGroup(Visualization.FOCUS_ITEMS).setTuple(item);
		
		item.setFixed(true);
		dragged = false;
		Display d = (Display)e.getComponent();
		down = d.getAbsoluteCoordinate(e.getPoint(), down);
		
		vis.run("forces");
	}
	
	public void itemReleased(VisualItem item, MouseEvent e) {
		if (!SwingUtilities.isLeftMouseButton(e)) return;
		if ( dragged ) {
			activeItem = null;
			item.setFixed(wasFixed);
			dragged = false;
		}
		// clear the focus
		Visualization vis = item.getVisualization();
		vis.getFocusGroup(Visualization.FOCUS_ITEMS).clear();

		vis.cancel("forces");
	}
	
	public void itemClicked(VisualItem item, MouseEvent e) {
		if (!SwingUtilities.isLeftMouseButton(e)) return;
		if ( e.getClickCount() == 2 ) {
			String id = item.getString("id");
			BrowserLauncher.showDocument(URL+id);
		}
	}
	
	public void itemDragged(VisualItem item, MouseEvent e) {
		if (!SwingUtilities.isLeftMouseButton(e)) return;
		dragged = true;
		Display d = (Display)e.getComponent();
		tmp = d.getAbsoluteCoordinate(e.getPoint(), tmp);
		double dx = tmp.getX()-down.getX();
		double dy = tmp.getY()-down.getY();
		
		PrefuseLib.setX(item, null, item.getX()+dx);
		PrefuseLib.setY(item, null, item.getY()+dy);
		down.setLocation(tmp);
		if ( repaint )
			item.getVisualization().repaint();
	}
} // end of inner class DataMountainControl
















	
